Replace textblob/nltk summary scoring with pure-Python heuristic#3571
Conversation
TextBlob (and its transitive NLTK dependency) were used in exactly one place: SummaryEvaluator, which ranks a work's candidate descriptions. Both projects are barely maintained, and NLTK has an unpatched path-traversal vulnerability, so this drops them entirely. SummaryEvaluator now scores summaries on their coverage of the work's consensus vocabulary -- content words recurring across at least two of its summaries -- instead of TextBlob noun phrases, and counts sentences with a regex instead of the NLTK tokenizer. This preserves the behavior that matters: an on-topic description still outranks generic marketing copy, which shares little vocabulary with the others. "Shorter is better" is now an explicit tie-breaker rather than an accident of TextBlob's part-of-speech tagging. Removing textblob also drops nltk, joblib, regex, and tqdm, and lets us delete the corpus-download steps from the Docker base image and tox.
|
Claude finished @jonathangreen's task in 6m 16s —— View job Code Review
SummaryClean, well-executed dependency removal. The consensus-vocabulary rewrite is mathematically sound: the coverage score is provably bounded to DetailsNit:
|
Greptile SummaryThis PR removes the
Confidence Score: 5/5Safe to merge; the change only affects which description is displayed when a work has multiple candidates, and the new algorithm is well-tested with no correctness concerns. The algorithmic replacement is logically sound: coverage is always in [0, 1], the df² weighting naturally elevates recurring words, the length nudge is provably bounded, and the zero-content-word edge case is handled explicitly. The test suite exercises all meaningful paths including the previously untested non-English penalty assertion. Dependency removal is clean across pyproject.toml, uv.lock, Dockerfile, and tox.ini with no residual references. No files require special attention. Important Files Changed
|
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #3571 +/- ##
==========================================
+ Coverage 93.47% 93.50% +0.03%
==========================================
Files 512 512
Lines 46619 46640 +21
Branches 6353 6358 +5
==========================================
+ Hits 43575 43612 +37
+ Misses 1968 1959 -9
+ Partials 1076 1069 -7 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
- Replace the unbounded length multiplier with a bounded nudge: length maps to a multiplier in [1 - length_nudge, 1] (length_nudge = 5%), so it can only break ties and never overturns a word-coverage difference (>= 10% per consensus word). Old 1/(1+len/n) shrank toward zero without limit, coupling its influence to absolute length. - Update the evaluate_summary_quality docstring to describe consensus vocabulary instead of the removed noun-phrase mechanism. - Add tests: length/coverage inversion regression, bounded-nudge guarantee, bytes input, dedup, unseen-summary scoring, custom vs default bad-phrase behavior, regex/dash penalties, empty best_choice (summary.py now 100%).
- Two-candidate works had an inert coverage signal: a word only reached the >= 2 'consensus' threshold when it appeared in both summaries, so every consensus word was trivially in both and each scored coverage 1.0 (or the set was empty and both hit the neutral 1.0). Ranking then collapsed onto sentence count and the length nudge, letting a generic blurb win. Replace the binary consensus set with recurrence-weighted coverage (weight = document frequency squared): words shared across more summaries dominate, incidental words still count a little, and the signal stays meaningful for two-candidate works. Drops the now-unused top_words_to_consider parameter. - Require word tokens to start with a letter so apostrophe-only runs like "'''" are no longer treated as content words. - Add regression tests for the two-candidate case and the no-content-words corpus; update the readiness assertion to total_word_weight.
_content_words and _count_sentences are only used by SummaryEvaluator, so make them static methods on the class, alongside the _word_re and _sentence_end_re patterns they use (now class attributes). No behavior change.
The generic blurb shared 'love' with the 'second' fixture, so it did cover a recurring word and the comment's 'covers none of the top words' claim was inaccurate. Swap 'love' for 'romance' so generic shares no vocabulary with the other descriptions and the test asserts what it claims.
Description
Removes the
textblobdependency (and its transitivenltk,joblib,regex, andtqdmpackages) by reimplementing the one place they were used:SummaryEvaluator, which ranks a work's candidate descriptions during presentation calculation.The evaluator now scores each summary on how well it covers the work's consensus vocabulary — content words (non-stopwords, via the repo's existing
ENGLISH_STOPWORDS) that recur across at least two of the work's summaries — instead of TextBlob noun phrases. Sentence counting uses a regex instead of the NLTK tokenizer. The other scoring terms (bad-phrase penalties, the home-grown English-bigram language penalty) are unchanged. "All else being equal, shorter is better" is now an explicit tie-breaker rather than an accident of TextBlob's part-of-speech tagging.Also removes the now-unneeded corpus-download steps from
docker/Dockerfile.baseimageandtox.ini.Motivation and Context
Both
textblobandnltkare barely maintained, andnltkhas an unpatched path-traversal vulnerability (nltk.data.load()decode-after-check via thenltk:URL scheme). They were the source of recurring dependency friction for a single, soft display-quality feature, so removing them outright is preferable to waiting on upstream fixes.The noun-phrase signal being replaced only ever affected which description is displayed when a work has multiple, and only ~27%+ of described works have more than one candidate. The consensus-vocabulary replacement preserves the behavior that actually matters — an on-topic description still outranks generic marketing copy, which shares little vocabulary with the other descriptions — with no heavy dependencies.
How Has This Been Tested?
tests/manager/core/test_summary_evaluator.py(7 passing), including a new case asserting generic marketing copy loses to an on-topic summary.evaluate_summary_qualityintegration test (tests/manager/sqlalchemy/model/test_edition.py) passes against Postgres/OpenSearch viatox -e py312-docker.mypyis clean.Checklist